#ifndef QLSAVDECODE_H
#define QLSAVDECODE_H

#define VIDEO_PICTURE_QUEUE_SIZE 1
#define AVCODEC_MAX_AUDIO_FRAME_SIZE 192000 // 1 second of 48khz 32bit audio

#define MAX_AUDIO_SIZE (25 * 16 * 1024)
#define MAX_VIDEO_SIZE (18 * 256 * 1024)

#include <QObject>
#include <QThread>
#include <QString>
#include <QImage>
#include <QMutex>
#include <QSemaphore>
#include <QPaintEvent>
#include <QtConcurrent>
#include <QFuture>

#include "qlsavpacketqueue.h"

class QLsAVPlayer;
class QLsAVParam;

/* 根据解码后的视频数据 计算出视频的pts */
double SynchronizeVideo(QLsAVParam *avParam, AVFrame *srcFrame, double pts);
/**
 * @brief RaiseVolume	音量调节
 * @param buf			需要调节音量的音频数据块首地址指针
 * @param size			长度
 * @param uRepeat		重复次数，通常设为1
 * @param vol			增益倍数,可以小于1
 */
void RaiseVolume(char* buf, int size, int uRepeat, double vol);
/* 视频解码线程 */
int VDecodeThread(void *arg);
/* 音频回调函数 */
void ACallback(void *userdata, Uint8 *stream, int len);
/* 音频解码函数 */
int ADecode(QLsAVParam *avParam);

/* 音视频处理参数封装 */
class QLsAVParam
{
public:
	QLsAVParam(QLsAVPlayer *avPlayer = nullptr);
	~QLsAVParam();

	/* 参数重置 */
	void ResetParam();

public:
	/* 音视频整体 */
	AVFormatContext *m_inputCtx;
	QLsAVPlayer *m_avPlayer;
	bool m_bDecodeThreadFinished;		/* 解码线程完毕 */
	bool m_bDecodeFinished;				/* 解码完毕 */
	bool m_bQuit;						/* 整体退出 */
	bool m_bPause;						/* 暂停 */
	bool m_bMute;						/* 静音 */
	int m_bSeek;						/* 跳转标志 */
	int64_t m_seekPos;					/* 跳转的位置(微秒) */
	double m_seekTime;					/* 跳转的时间(秒)(值为：m_seekPos / 1e6) */
	quint64 m_startTime;				/* 开始播放视频的时间 */
	quint64 m_pauseStartTime;			/* 暂停开始的时间 */
	bool m_bFirstPause;					/* 第一次显示是否暂停(为了加载完视频有个预览界面) */

	/* 视频 */
	int m_vStreamIndex;					/* 视频里位置 */
	AVCodecContext *m_vCodecCtx;		/* 视频流信息 */
	AVCodec *m_vCodec;					/* 视频流解码器 */
	AVStream *m_vStream;				/* 视频流 */
	QLsAVPacketQueue *m_vPktQueue;		/* 视频数据包缓冲区队列 */
	QFuture<void> m_vDecodeThread;		/* 视频解码线程 */
	bool m_bVDecodeThreadFinished;		/* 视频解码线程是否完成 */
	double m_vClock;					/* 视频时钟 */
	bool m_bVSeek;						/* 视频跳转标志 */

	/* 音频 */
	int m_aStreamIndex;					/* 音频流位置 */
	AVCodecContext *m_aCodecCtx;		/* 音频流信息 */
	AVCodec *m_aCodec;					/* 音频流解码器 */
	AVStream *m_aStream;				/* 音频流 */
	QLsAVPacketQueue *m_aPktQueue;		/* 音频数据包缓冲队列 */
	AVPacket m_aPacket;					/* 音频数据包 */
	bool m_bADecodeThreadFinished;		/* 音频解码线程是否完成 */
	AVFrame *m_aFrame;					/* 解码出的音频数据 */
	float m_volume;						/* 音量增益系数 */
	double m_aClock;					/* 音频时钟 */
	bool m_bASeek;						/* 音频跳转标志 */
	AVFrame *m_aFrameReSample;			/* 重采样后的解码音频数据 */
	SwrContext *m_aSwrCtx;				/* 用于解码后的音频格式转换 */

	/* 音频解码 */
	unsigned int audio_buf_size;
	unsigned int audio_buf_index;
	uint8_t *audio_pkt_data;
	int audio_pkt_size;
	uint8_t *audio_buf;
	DECLARE_ALIGNED(16,uint8_t,audio_buf2) [AVCODEC_MAX_AUDIO_FRAME_SIZE * 4];
	AVSampleFormat audio_src_fmt;
	AVSampleFormat audio_tgt_fmt;
	int audio_src_channels;
	int audio_tgt_channels;
	int64_t audio_src_channel_layout;
	int64_t audio_tgt_channel_layout;
	int audio_src_freq;
	int audio_tgt_freq;
	int audio_hw_buf_size;

	/* 重采样 */
	AVSampleFormat in_sample_fmt; //输入的采样格式
	AVSampleFormat out_sample_fmt;//输出的采样格式 16bit PCM
	int in_sample_rate;//输入的采样率
	int out_sample_rate;//输出的采样率
	int in_ch_layout;
	int out_ch_layout;
};

/* 音视频解码 */
class QLsAVPlayer : public QObject
{
	Q_OBJECT
public:
	/* 播放状态 */
	enum QLsPlayerStatus
	{
		Player_Default = 0,		/* 默认 */
		Player_Playing,			/* 播放中 */
		Player_Pause,			/* 暂停 */
		Player_Stop				/* 停止 */
	};

public:
	QLsAVPlayer(QObject *parent = nullptr);
	~QLsAVPlayer();

	/* ---------- 多线程信号槽 ---------- */
	/* 加载 */
	void Load();
	/* 显示一帧图像 */
	void DisplayVideo(QImage img);
	/* 重新播放 */
	bool Replay();
	/* 播放 */
	bool Play();
	/* 暂停 */
	bool Pause();
	/* 停止 */
	bool Stop(bool bWait = false);
	/* 跳转(微秒) */
	void Seek(qint64 pos);
	/* 静音 */
	bool mute() const;
	void setMute(const bool &bMute);
	/* 音量 */
	void SetVolume(const float &volume);

	/* 路径 */
	QString AVUrl() const;
	void setAVUrl(const QString &AVUrl);

	/* 音视频播放状态 */
	QLsPlayerStatus playerStatus() const;

	/* 播放进度/当前时间 */
	qint64 curTime() const;

	/* 总时长 */
	qint64 totalTime() const;

	/* 设置是否显示预览(第一次加载视频是否暂停) */
	void setPreview(const bool &bFirstPause);

signals:
	/* 获取一帧图像 的信号 */
	void sigGetOneFrame(QImage);
	/* 状态变化 的信号 */
	void sigStatusChange(QLsPlayerStatus);
	/* 视频时长变化 的信号 */
	void sigTotalTimeChange(qint64);

private:
	/* 解码开始 */
	void DecodeStart();
	/* 解码结束 */
	void DecodeEnd();
	/**
	 * SDL打开音频设备
	 * 打开SDL，并设置播放的格式为：AUDIO_S16LSB、双声道、44100Hz
	 * 后期使用 ffmpeg 解码完音频后，需要重采样成和这个一样的格式，否则播放会有杂音
	 */
	bool SDLOpenADevice();
	/* SDL关闭音频设备 */
	void SDLCloseADevice();
	/* 打开要解码的音视频流 */
	bool AVOpenInput(const QString &url);
	/* 获取音视频流的位置信息 */
	void AVGetStreamIndex();
	/* 获取音频流信息 */
	bool AVGetAStreamParam();
	/* 获取视频流信息 */
	bool AVGetVStreamParam();
	/* 解码跳转 */
	void AVDecodeSeek();
	/* 解码 */
	void Decoding();


private slots:
	/* ---------- 多线程信号槽 ---------- */
	/* 加载 的槽函数 */
	void slotLoad();

signals:
	/* ---------- 多线程信号槽 ---------- */
	/* 加载 的信号 */
	void sigLoad();

private:
	QThread *m_thread;
	QString m_AVUrl;					/* 路径 */
	QLsAVParam *m_avParam;				/* 音视频处理参数封装 */
	QLsPlayerStatus m_playerStatus;		/* 音视频播放状态 */
	SDL_AudioDeviceID m_aDeviceID;		/* 音频设备id */
};

#endif // QLSAVDECODE_H
